﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Text.RegularExpressions;

namespace LaunchPad.Core.Maths
{
    public struct Distance
    {
        public Distance(double millimeters) : this()
        {
            Millimeters = millimeters;
        }

        [XmlAttribute]
        public double Millimeters { get; set; }

        [XmlIgnore]
        public double Centimeters
        {
            get { return Millimeters * 0.1; }
            set { Millimeters = value * 10; }
        }

        [XmlIgnore]
        public double Meters
        {
            get { return Millimeters * 0.001; }
            set { Millimeters = value * 1000.0; }
        }

        [XmlIgnore]
        public double Feet
        {
            get { return Millimeters * 0.0032808399; }
            set { Millimeters = value * 304.8; }
        }

        [XmlIgnore]
        public double Inches
        {
            get { return Millimeters * 0.0393700787; }
            set { Millimeters = value * 25.4; }
        }

        [XmlIgnore]
        public double Miles
        {
            get { return Millimeters * 6.21371192e-7; }
            set { Millimeters = value * 1609344.0; }
        }

        [XmlIgnore]
        public double SoundSeconds
        {
            get { return Millimeters * 2.913752913752914e-6; }
            set { Millimeters = value * 343200.0; }
        }

        public static bool operator ==(Distance d1, Distance d2)
        {
            return d1.Millimeters == d2.Millimeters;
        }

        public static bool operator !=(Distance d1, Distance d2)
        {
            return d1.Millimeters != d2.Millimeters;
        }

        public static Distance operator +(Distance d1, Distance d2)
        {
            return new Distance(d1.Millimeters + d2.Millimeters);
        }

        public static Distance operator -(Distance d1, Distance d2)
        {
            return new Distance(d1.Millimeters - d2.Millimeters);
        }

        public static Distance operator /(Distance d, double n)
        {
            return new Distance(d.Millimeters / n);
        }

        public static Distance operator *(Distance d, double n)
        {
            return new Distance(d.Millimeters * n);
        }

        public static Distance MaxValue
        {
            get { return new Distance(double.MaxValue); }
        }

        public static Distance MinValue
        {
            get { return new Distance(double.MinValue); }
        }

        public static Distance Zero
        {
            get { return new Distance(); }
        }

        public static Distance FromMillimeters(double millimeters)
        {
            return new Distance(millimeters);
        }

        public static Distance FromCentimeters(double centimeters)
        {
            return new Distance { Centimeters = centimeters };
        }

        public static Distance FromMeters(double meters)
        {
            return new Distance { Meters = meters };
        }

        public static Distance FromFeet(double feet)
        {
            return new Distance { Feet = feet };
        }

        public static Distance FromInches(double inches)
        {
            return new Distance { Inches = inches };
        }

        public static Distance FromMiles(double miles)
        {
            return new Distance { Miles = miles };
        }

        public static Distance FromSoundSeconds(double soundSeconds)
        {
            return new Distance { SoundSeconds = soundSeconds };
        }

        public static bool TryParse(string value, out Distance distance)
        {
            distance = Distance.Zero;

            var ds = new string(value.TakeWhile(c => char.IsDigit(c) || c == '.').ToArray());
            var unit = value.Substring(ds.Length).Trim().ToLower();
            var d = double.Parse(ds);

            switch (unit)
            {
                case "millimeters":
                case "millimeter":
                case "mm":
                    distance = Distance.FromMillimeters(d);
                    break;
                case "meters":
                case "meter":
                case "m":
                    distance = Distance.FromMeters(d);
                    break;
                case "miles":
                case "mile":
                case "mi":
                    distance = Distance.FromMiles(d);
                    break;
                case "foot":
                case "feet":
                case "ft":
                case "'":
                    distance = Distance.FromFeet(d);
                    break;
                case "inches":
                case "inch":
                case "in":
                case "\"":
                    distance = Distance.FromInches(d);
                    break;
                default:
                    return false;
            }

            return true;
        }

        public override string ToString()
        {
            return string.Format("{0:F2} m", Meters);
        }
    }
}
